// -*-c++-*- Copyright (C) 2011 osgPango Development Team
// $Id: GlyphRenderer 168 2011-09-29 17:13:14Z cubicool $

#ifndef OSGPANGO_GLYPHRENDERER
#define OSGPANGO_GLYPHRENDERER

#include <osg/Geometry>
#include <osg/Texture2D>
#include <osgCairo/Image>
#include <osgPango/Export>
#include <osgPango/GlyphLayer>
#include <osgPango/Glyph>

namespace osgPango {

//! This object packages up all the state stuff we need to shimmy around during rendering
//! and Text creation.
struct OSGPANGO_EXPORT GlyphGeometryState {
	std::vector<osg::Texture*> textures;
	std::vector<osg::Vec3>     colors;
};

typedef std::pair<osg::Geometry*, GlyphGeometryState> GeometryAndState;
typedef std::list<GeometryAndState>                   GeometryList;

const short DEFAULT_TEXTURE_WIDTH  = 256;
const short DEFAULT_TEXTURE_HEIGHT = 256;

//! Our base class for all GlyphRenderer objects. This object doesn't actually know how to
//! draw anything; use GlyphRendererDefault for that.
class OSGPANGO_EXPORT GlyphRenderer: public osg::Object {
public:
	//! Maps the result of _hashFont() to a GlyphCache atlas object. Each GlyphRenderer
	//! maintains its own cache, which diverges greatly from the API in 1.0.0.
	typedef std::map<guint, osg::ref_ptr<GlyphCache> > FontGlyphCacheMap;

	META_Object(osgPango, GlyphRenderer);

	GlyphRenderer();
	GlyphRenderer(const GlyphRenderer& gr, const osg::CopyOp& copyOp);

	//! This method returns an osg::Vec4 object whose values correspond to the following:
	//! the x origin offset, y origin offset, extra width, and extra height respectively.
	//! It is used when custom "effects" implementations needs to inform the cache object
	//! that additional space should be required for proper positioning.
	virtual osg::Vec4 getExtraGlyphExtents() const;
	
	//! Requests that the GlyphRenderer rasterize the passed in glyph into the GlyphLayer
	//! index defined by @layer by calling the correspond GlyphLayer::render method. Returns
	//! whether or not this was successful.
	virtual bool renderLayer(
		unsigned int   layer,
		cairo_t*       c,
		cairo_glyph_t* glyph,
		unsigned int   width,
		unsigned int   height
	) const;
	
	//! The default version of this method sets the vertex shader and texture uniforms for
	//! the Geode in question. The pass number, however, is not used in the default implementation,
	//! though custom Renderers often need this.
	virtual bool updateOrCreateState(int pass, osg::Geode* geode) const;

	//! This method sets the color uniforms for the passed-in geometry, which we can assume has
	//! already been sorted properly prior to calling this routine.
	virtual bool updateOrCreateState(osg::Geometry* geometry, const GlyphGeometryState& state) const;

	//! Allows the user to retrieve a RW pointer to the internal GlyphCache using a
	//! PangoFont pointer. If the GlyphCache has not yet been created, a null pointer
	//! will be returned.
	GlyphCache* getGlyphCache(PangoFont* font) const;

	//! This method behaves identically to getGlyphCache, except that if the GlyphCache
	//! objet has not yet been created it will be.
	GlyphCache* getOrCreateGlyphCache(PangoFont* font);

	//! Returns a RW reference to the internal GlyphCache map; this should really
	//! only EVER be needed by the serializer; if you're calling it directly, something
	//! is very wrong.
	FontGlyphCacheMap& getGlyphCaches() {
		return _gc;
	}

	//! Returns a RO reference to the internal GlyphCache map.
	const FontGlyphCacheMap& getGlyphCaches() const {
		return _gc;
	}

	//! This will allow you to change (dynamically as you see fit) the size of the
	//! glyph caching textures used internally. The compiled-in defaults (which
	//! you can change in Context.cpp) are 256x256. However, you may have some
	//! bizarre case where you need more. :)
	void setTextureSize(const osg::Vec2s& size) {
		_textureSize = size;
	}
	
	const osg::Vec2s& getTextureSize() const {
		return _textureSize;
	}

	//! This method simple creates and returns a new Texture2D object using the @image argument
	//! as its data source. You could override this method to enable mipmapping by default, for example.
	virtual osg::Texture2D* createTexture(osg::Image* image) const;

	unsigned int getPixelSpacing() const {
		return _pixelSpacing;
	}

	//! Set the number of pixels to "pad" the active texture region per glyph; the default is 1.
	void setPixelSpacing(unsigned int spacing) {
		_pixelSpacing = spacing;
	}

	//! TODO: Mipmaps...
	osg::Texture::FilterMode getMinFilterMode() const {
		return _minFilter;
	}

	//! Sets the textures minFilterMode; this can be used to enable mipmapping, though it should
	//! be remembered to also call setPixelSpacing. TODO: Finish this...
	void setMinFilterMode(osg::Texture::FilterMode fm) {
		_minFilter = fm;
	}

	GlyphLayer* getLayer(unsigned int layer);

	const GlyphLayer* getLayer(unsigned int layer) const;

	//! Appends a GlyphLayer object onto the container of combined layers.
	void addLayer(GlyphLayer* layer);

	//! Removes the layer at the index specified by @layer; this calls std::vector::erase, so
	//! all subsequent layers will be "bumped down."
	void removeLayer(unsigned int layer);

	//! Replaces the layer at index @index with the new GlyphLayer object, @layer. The previous
	//! GlyphLayer, if not used elsewhere, will be deleted.
	void replaceLayer(unsigned int index, GlyphLayer* layer);

	//! Clears the entire GlyphLayer vector.
	void clearLayers();

	virtual unsigned int getNumLayers() const {
		return _layers.size();
	}

	//! Returns the number of render "passes" this GlyphRenderer should make. This is an advanced
	//! concept and is usually used by GlyphRenderer's that need to affect buffers other than the
	//! framebuffer.
	virtual unsigned int getNumPasses() const { 
		return 1;
	}

	//! Returns the CAIRO_FORMAT_* enum for the layer at index @layer.
	virtual cairo_format_t getImageFormatForLayer(unsigned int layer) const;

protected:
	bool _setFragmentShader(osg::Geode*, const std::string&) const;
	
private:
	guint _hashFont     (PangoFont* font) const;
	char* _describeFont (PangoFont* font) const;

	std::vector<osg::ref_ptr<GlyphLayer> > _layers;
	unsigned int                           _pixelSpacing;
	osg::Vec2s                             _textureSize;
	osg::Texture::FilterMode               _minFilter;
	FontGlyphCacheMap                      _gc;
};

class OSGPANGO_EXPORT GlyphRendererDefault: public GlyphRenderer {
public:
	META_Object(osgPango, GlyphRendererDefault);

	GlyphRendererDefault();
	GlyphRendererDefault(const GlyphRendererDefault& gr, const osg::CopyOp& copyOp);
	
	virtual bool updateOrCreateState(int pass, osg::Geode* geode) const;
};

const unsigned int DEFAULT_OUTLINE = 2;

class OSGPANGO_EXPORT GlyphRendererOutline: public GlyphRenderer {
public:
	META_Object(osgPango, GlyphRendererOutline);

	GlyphRendererOutline(unsigned int outline = DEFAULT_OUTLINE);
	GlyphRendererOutline(const GlyphRendererOutline& gro, const osg::CopyOp& copyOp);

	virtual bool updateOrCreateState(int pass, osg::Geode* geode) const;
};

const int DEFAULT_SHADOW_XOFFSET = 1;
const int DEFAULT_SHADOW_YOFFSET = 1;

class OSGPANGO_EXPORT GlyphRendererShadow: public GlyphRenderer {
public:
	META_Object(osgPango, GlyphRendererShadow);

	GlyphRendererShadow(
		int xOffset = DEFAULT_SHADOW_XOFFSET,
		int yOffset = DEFAULT_SHADOW_YOFFSET
	);

	GlyphRendererShadow(const GlyphRendererShadow& grs, const osg::CopyOp& copyOp);

	virtual bool updateOrCreateState(int pass, osg::Geode*) const;
};

const int          DEFAULT_SHADOW_BLUR_XOFFSET   = 0;
const int          DEFAULT_SHADOW_BLUR_YOFFSET   = 0;
const unsigned int DEFAULT_SHADOW_BLUR_RADIUS    = 2;
const unsigned int DEFAULT_SHADOW_BLUR_DEVIATION = 0;

class OSGPANGO_EXPORT GlyphRendererShadowBlur: public GlyphRenderer {
public:
	GlyphRendererShadowBlur(int = 0, int = 0, unsigned int = 2, unsigned int = 0);
	
	virtual bool updateOrCreateState(int pass, osg::Geode*) const;
};

const unsigned int DEFAULT_SHADOW_INSET_XOFF      = 0;
const unsigned int DEFAULT_SHADOW_INSET_YOFF      = 0;
const unsigned int DEFAULT_SHADOW_INSET_RADIUS    = 2;
const unsigned int DEFAULT_SHADOW_INSET_DEVIATION = 0;

class OSGPANGO_EXPORT GlyphRendererShadowInset: public GlyphRenderer {
public:
	GlyphRendererShadowInset(int = 0, int = 0, unsigned int = 2, unsigned int = 0);
	
	virtual bool updateOrCreateState(int pass, osg::Geode*) const;
};

const unsigned int DEFAULT_DISTANCE_FIELD_SCAN_SIZE         = 150;
const unsigned int DEFAULT_DISTANCE_FIELD_BLOCK_SIZE        = 32;
const double       DEFAULT_DISTANCE_FIELD_PADDING           = 3.0f;
const double       DEFAULT_DISTANCE_FIELD_SCALE_DENOMINATOR = 1.0f;

//! TODO: This is a cool class, is going to require a lot of documentation. :)
class OSGPANGO_EXPORT GlyphRendererDistanceField: public GlyphRenderer {
public:
	GlyphRendererDistanceField(
		unsigned int scanSize,
		unsigned int blockSize,
		double       padding,
		double       scaleDenominator = 1.0f
	);
	
	virtual bool updateOrCreateState (int pass, osg::Geode* geode) const;
	virtual void updateScaleState    (osg::Vec3::value_type scale, osg::StateSet* state) const;

	void setScaleDenominator(double scaleDenominator) {
		_scaleDenominator = scaleDenominator;
	}

	double getScaleDenominator() const {
		return _scaleDenominator;
	}

protected:
	double _scaleDenominator;
};

}

#endif
